package com.gitee.apanlh.util.thread;

import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.reflection.ClassUtils;

import java.util.List;

/**	
 * 	堆栈跟踪工具类
 * 
 * 	@author Pan
 */
public class StackUtils {
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private StackUtils() {
		//	不允许外部实例
		super();
	}
	
	/**
     * 	获取调用堆栈的所有堆栈帧。
     * 	
     * 	@author Pan
     * 	@return StackTraceElement[]	包含所有堆栈帧的数组。
     */
    public static StackTraceElement[] getStackTrace() {
        return Thread.currentThread().getStackTrace();
    }
	
	/**	
	 * 	获取当前调用者的Class
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String getCallClassName() {
		StackTraceElement[] stackTrace = getStackTrace();
		return stackTrace[stackTrace.length - 1].getClassName();
	}
	
	/**	
	 * 	获取当前调用者的方法名
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String getCallMethodName() {
		StackTraceElement[] stackTrace = getStackTrace();
		return stackTrace[stackTrace.length - 1].getMethodName();
	}
	
	/**	
	 * 	获取当前调用者的行号
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static int getCallLineNumber() {
		StackTraceElement[] stackTrace = getStackTrace();
		return stackTrace[stackTrace.length - 1].getLineNumber();
	}
	
	/**	
	 * 	获取当前调用者的文件名
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String getCallFileName() {
		StackTraceElement[] stackTrace = getStackTrace();
		return stackTrace[stackTrace.length - 1].getFileName();
	}
	
	/**	
	 * 	获取调用方法名称
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String getMethodName() {
		return Thread.currentThread().getName();
	}
	
	/**
     * 	获取调用堆栈中的所有类名信息
     * 	
     * 	@author Pan
     * 	@return List	包含所有类名的列表
     */
    public static List<String> getAllClassName() {
    	StackTraceElement[] stackTrace = getStackTrace();
    	return CollUtils.newArrayList(list -> IteratorUtils.array(stackTrace, element -> list.add(element.getClassName())), stackTrace.length);
    }
    
    /**
     * 	获取调用堆栈中的所有方法名
     * 	
     * 	@author Pan
     * 	@return List	包含所有方法名的列表。
     */
    public static List<String> getAllMethodName() {
    	StackTraceElement[] stackTrace = getStackTrace();
    	return CollUtils.newArrayList(list -> IteratorUtils.array(stackTrace, element -> list.add(element.getMethodName())), stackTrace.length);
    }
    
    /**
     * 	根据指定类名获取调用堆栈中的所有堆栈帧信息
	 * 
     * 	@author Pan
     * 	@param  clazz 	类
     * 	@return List	包含指定类名的所有堆栈帧的列表
     */
    public static List<StackTraceElement> getAllStackTraceByClassName(Class<?> clazz) {
    	return getAllStackTraceByClassName(ClassUtils.getName(clazz));
    }
    
    /**
     * 	根据指定类名获取调用堆栈中的所有堆栈帧信息
     * 	<br>如存在全限定名则equals判断
	 * 	<br>如不存在则只验证类名的结尾是否一致
	 * 
     * 	@author Pan
     * 	@param  className 	类名
     * 	@return List	    包含指定类名的所有堆栈帧的列表
     */
    public static List<StackTraceElement> getAllStackTraceByClassName(String className) {
    	StackTraceElement[] stackTrace = getStackTrace();
    	boolean isFullyQualifiedClassName = ClassUtils.isFullyQualifiedClassName(className);
    	
        return CollUtils.newArrayList(list -> 
        	IteratorUtils.array(stackTrace, element -> {
        		if (isFullyQualifiedClassName) {
    				if (Eq.str(element.getClassName(), className)) {
    					list.add(element);
    				}
    			} else {
    				if (element.getClassName().endsWith(className)) {
    					list.add(element);
    				}
    			}
        	})
        , stackTrace.length);
    }
    
    /**
     * 	根据指定方法名获取调用堆栈中的所有堆栈帧信息
     * 	
     * 	@author Pan
     * 	@param  methodName 方法名
     * 	@return List       包含指定方法名的所有堆栈帧的列表
     */
    public static List<StackTraceElement> getAllStackTraceByMethodName(String methodName) {
    	StackTraceElement[] stackTrace = getStackTrace();
    	
        return CollUtils.newArrayList(list -> 
        	IteratorUtils.array(stackTrace, element -> {
        		if (Eq.str(element.getMethodName(), methodName)) {
        			list.add(element);
    			}
        	})
        , stackTrace.length);
    }
    
    /**	
	 * 	根据某个关键字获取堆栈帧信息
	 * 	
	 * 	@author Pan
	 * 	@param 	keyword		关键字
	 * 	@return	List
	 */
    public static List<StackTraceElement> getAllStackTraceByKeyword(String keyword) {
    	StackTraceElement[] stackTrace = getStackTrace();
    	
        return CollUtils.newArrayList(list -> 
        	IteratorUtils.array(stackTrace, element -> {
        		if (StringUtils.contains(element.toString(), keyword)) {
        			list.add(element);
    			}
        	})
        , stackTrace.length);
    }
	
    /**	
	 * 	根据类名获取首次出现的堆栈帧信息
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz	类
	 * 	@return	StackTraceElement
	 */
    public static StackTraceElement getStackTraceByClassName(Class<?> clazz) {
    	return getStackTraceByClassName(ClassUtils.getName(clazz));
    }
	
    /**	
	 * 	根据类名获取首次出现的堆栈帧信息
	 * 	<br>如存在全限定名则equals判断
	 * 	<br>如不存在则只验证类名的结尾是否一致
	 * 	
	 * 	@author Pan
	 * 	@param 	className	类名
	 * 	@return	StackTraceElement
	 */
	public static StackTraceElement getStackTraceByClassName(String className) {
		StackTraceElement[] stackTrace = getStackTrace();
		boolean isFullyQualifiedClassName = ClassUtils.isFullyQualifiedClassName(className);
		
		for (int i = 0; i < stackTrace.length; i++) {
			StackTraceElement element = stackTrace[i];
			
			if (isFullyQualifiedClassName) {
				if (Eq.str(element.getClassName(), className)) {
					return element;
				}
			} else {
				if (element.getClassName().endsWith(className)) {
					return element;
				}
			}
		}
		return null;
	}
	
	/**	
	 * 	根据方法名获取首次出现的堆栈帧信息
	 * 	
	 * 	@author Pan
	 * 	@param 	methodName 方法名
	 * 	@return	StackTraceElement
	 */
	public static StackTraceElement getStackTraceByMethodName(String methodName) {
		StackTraceElement[] stackTrace = getStackTrace();
		
		for (int i = 0; i < stackTrace.length; i++) {
			StackTraceElement element = stackTrace[i];
			if (Eq.str(element.getMethodName(), methodName)) {
				return element;
			}
		}
		return null;
	}
	
	/**	
	 * 	根据某个关键字获取首次出现的堆栈帧信息
	 * 	
	 * 	@author Pan
	 * 	@param 	keyword		关键字
	 * 	@return	StackTraceElement
	 */
	public static StackTraceElement getStackTraceByKeyword(String keyword) {
		StackTraceElement[] stackTrace = getStackTrace();
		
		for (int i = 0; i < stackTrace.length; i++) {
			StackTraceElement element = stackTrace[i];
			if (StringUtils.contains(element.toString(), keyword)) {
				return element;
			}
		}
		return null;
	}
}
